package com.yuke.cloud.service.uac.service.impl;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.yuke.cloud.common.base.constant.GlobalConstant;
import com.yuke.cloud.common.base.dto.LoginAuthDto;
import com.yuke.cloud.common.base.enums.ErrorCodeEnum;
import com.yuke.cloud.common.util.PublicUtil;
import com.yuke.cloud.service.api.uac.exceptions.UacBizException;
import com.yuke.cloud.service.uac.constant.MenuConstant;
import com.yuke.cloud.service.uac.dao.AccMenuMapper;
import com.yuke.cloud.service.uac.dto.AccMenuEnableDto;
import com.yuke.cloud.service.uac.entity.AccMenu;
import com.yuke.cloud.service.uac.entity.AccRoleMenu;
import com.yuke.cloud.service.uac.service.AccMenuService;
import com.yuke.cloud.common.core.support.BaseService;
import com.yuke.cloud.service.uac.service.AccPermissionService;
import com.yuke.cloud.service.uac.service.AccRoleMenuService;
import com.yuke.cloud.service.uac.service.AccUserRoleService;
import com.yuke.cloud.service.uac.utils.TreeUtil;
import com.yuke.cloud.service.uac.vo.MenuVo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.util.*;


/**
 * Created by wg on 2018/10/31.
 */
@Service
@Transactional
public class AccMenuServiceImpl extends BaseService<AccMenu> implements AccMenuService {
    @Resource
    private AccMenuMapper accMenuMapper;
    @Resource
    private AccRoleMenuService accRoleMenuService;
    @Resource
    private AccPermissionService accPermissionService;
    @Resource
    private AccUserRoleService accUserRoleService;

    private List<AccMenu> getMenuListByUserId(Long userId, Long applicationId, Boolean isEnable) {

        if (userId == null) {
            throw new UacBizException(ErrorCodeEnum.UAC10011001);
        }

        // 1.查询该用户下所有的菜单列表
        List<AccMenu> menuList;
        Set<AccMenu> menuSet = Sets.newHashSet();
        // 如果是admin则返回所有的菜单
        // mod by wg 20181220 当用户是管理员角色时也获取所有菜单列表
        if (userId == GlobalConstant.Sys.SUPER_MANAGER_USER_ID || accUserRoleService.checkAdminByUserId(userId)) {
            // 1.1 查询该用户下所有的菜单列表
            Example example = new Example(AccMenu.class);
            Example.Criteria criteria= example.createCriteria();
//            criteria.andEqualTo("isEnable", true).andEqualTo("applicationId",applicationId);
            if (PublicUtil.isNotEmpty(isEnable)) {
                criteria.andEqualTo("isEnable", isEnable);
            }
            example.setOrderByClause("level asc,priority asc");

            menuList = accMenuMapper.selectByExample(example);
        } else {
            // 1.2查询该用户下所有的菜单列表
            List<AccMenu> ownMenuList = accMenuMapper.findMenuListByUserId(userId);
            if (PublicUtil.isEmpty(ownMenuList)) {
                return null;
            }

            // 查出所有含有菜单的菜单信息
            Example example = new Example(AccMenu.class);
            Example.Criteria criteria= example.createCriteria();
            criteria.andEqualTo("isEnable", true);
            List<AccMenu> allMenuList = accMenuMapper.selectByExample(example);

            Map<Long, AccMenu> map = Maps.newHashMap();
            for (final AccMenu menu : allMenuList) {
                map.put(menu.getMenuId(), menu);
            }

            // add by wg 20181225 去掉重复的菜单作准备
            Map<Long, AccMenu> mapOwnMenu = Maps.newHashMap();
            for (final AccMenu menu : ownMenuList) {
                mapOwnMenu.put(menu.getMenuId(), menu);
            }

            for (final AccMenu menu : ownMenuList) {
                getPid(menuSet, menu, map, mapOwnMenu);
            }
            menuList = new ArrayList<>(menuSet);

            menuList.addAll(ownMenuList);
        }

        return menuList;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<AccMenu> getAllMenuListByUserId(Long userId, Long applicationId) {
        return this.getMenuListByUserId(userId, applicationId, null);
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<MenuVo> getOwnMenuListByUserId(Long userId, Long applicationId) {
        List<AccMenu> menuList = this.getMenuListByUserId(userId, applicationId, true);

        if (PublicUtil.isNotEmpty(menuList)) {
            List<MenuVo> list = getMenuVo(menuList);
            return TreeUtil.getChildMenuVos(list, 0L);
        }

        return null;
    }

    private void getPid(Set<AccMenu> menuSet, AccMenu menu, Map<Long, AccMenu> map, Map<Long, AccMenu> mapOwnMenu) {
        AccMenu parent = map.get(menu.getParentId());
        if (parent != null && parent.getMenuId() != 0L) {
            // mod by wg 20181225 去掉重复部分
            if (mapOwnMenu.get(parent.getMenuId()) == null) {
                menuSet.add(parent);
            }
            getPid(menuSet, parent, map, mapOwnMenu);
        }
    }

    private List<MenuVo> getMenuVo(List<AccMenu> list) {
        List<MenuVo> menuVoList = Lists.newArrayList();
        for (AccMenu accMenu : list) {
            MenuVo menuVo = new MenuVo();
            BeanUtils.copyProperties(accMenu, menuVo);
            menuVo.setUrl(accMenu.getUrl());
            menuVo.setMenuName(accMenu.getMenuName());
            menuVoList.add(menuVo);
        }
        return menuVoList;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public AccMenu getViewMenuById(Long id) {
        Preconditions.checkArgument(id != null, "菜单ID不能为空");
        AccMenu menu = accMenuMapper.selectByPrimaryKey(id);

        if (menu == null) {
            logger.error("找不到菜单信息id={}", id);
            throw new UacBizException(ErrorCodeEnum.UAC10013003, id);
        }

        // 获取父级菜单信息
        AccMenu parentMenu = accMenuMapper.selectByPrimaryKey(menu.getParentId());

        if (parentMenu != null) {
            menu.setParentMenuName(parentMenu.getMenuName());
        }

        return menu;
    }

    @Override
    public int saveAccMenu(AccMenu menu, LoginAuthDto loginAuthDto) {
        Long pid = menu.getParentId();
        AccMenu parentMenu = mapper.selectByPrimaryKey(pid);
        if (PublicUtil.isEmpty(parentMenu)) {
            throw new UacBizException(ErrorCodeEnum.UAC10013001, pid);
        }
        if (null == menu.getMenuId()) {  // 新增

            AccMenu updateMenu = new AccMenu();
            menu.setLevel(parentMenu.getLevel() + 1);
            updateMenu.setIsLeaf(MenuConstant.MENU_LEAF_NO);
            updateMenu.setMenuId(pid);
            Long menuId = super.generateId();
            menu.setMenuId(menuId);
            int result = mapper.updateByPrimaryKeySelective(updateMenu);  // 修改父节点信息
            if (result < 1) {
                throw new UacBizException(ErrorCodeEnum.UAC10013002, menuId);
            }

            menu.setIsEnable(true);
            menu.setCreateId(loginAuthDto.getUserId());
            menu.setCreateTime(new Date());
            // 新增的菜单是叶子节点
            menu.setIsLeaf(MenuConstant.MENU_LEAF_YES);
            accMenuMapper.insertSelective(menu);

            // add by wg 20190115 如果父节点被绑定给某一个角色后，则新增的节点也自动绑定给父节点对应的角色
            Example example = new Example(AccRoleMenu.class);
            Example.Criteria criteria= example.createCriteria();
            criteria.andEqualTo("menuId", pid);
            List<AccRoleMenu> listAccRoleMenu = accRoleMenuService.selectByExample(example);
            if (PublicUtil.isNotEmpty(listAccRoleMenu)) {
                for (AccRoleMenu accRoleMenu : listAccRoleMenu) {
                    if (PublicUtil.isNotEmpty(accRoleMenu.getRoleId())) {
                        AccRoleMenu accRoleMenuTemp = new AccRoleMenu();
                        accRoleMenuTemp.setRoleId(accRoleMenu.getRoleId());
                        accRoleMenuTemp.setRoleMenuId(super.generateId());
                        accRoleMenuTemp.setMenuId(menuId);
                        accRoleMenuService.save(accRoleMenuTemp);
                    }
                }
            }

            return 1;  // 成功插入1条记录
        } else {
            menu.setUpdateId(loginAuthDto.getUserId());
            menu.setUpdateTime(new Date());
            return accMenuMapper.updateByPrimaryKeySelective(menu);
        }
    }

    @Override
    public int deleteAccMenuById(Long id) {
        Preconditions.checkArgument(id != null, "菜单id不能为空");
        int result;
        // 获取当前菜单信息
        AccMenu accMenuQuery = new AccMenu();
        accMenuQuery.setMenuId(id);
        accMenuQuery = mapper.selectOne(accMenuQuery);
        if (PublicUtil.isEmpty(accMenuQuery)) {
            throw new UacBizException(ErrorCodeEnum.UAC10013003, id);
        }

        // 删除菜单与角色的关联关系
        AccRoleMenu accRoleMenu = new AccRoleMenu();
        accRoleMenu.setMenuId(id);
        accRoleMenuService.delete(accRoleMenu);

        // 删除权限
        // TODO 应该先查询再删除
        accPermissionService.deleteByMenuId(id);

        // 删除菜单
        result = accMenuMapper.deleteByPrimaryKey(id);
        if (result < 1) {
            logger.error("删除菜单失败 menuId={}", id);
            throw new UacBizException(ErrorCodeEnum.UAC10013008, id);
        }

        // 修改当前删除菜单的父菜单是否是叶子节点
        AccMenu updateParentUacMenu = new AccMenu();
        updateParentUacMenu.setMenuId(accMenuQuery.getParentId());
        updateParentUacMenu.setIsLeaf(MenuConstant.MENU_LEAF_YES);
        // 是二三级
        if (Objects.equals(MenuConstant.MENU_LEVEL_TWO, accMenuQuery.getLevel()) || Objects.equals(MenuConstant.MENU_LEVEL_THREE, accMenuQuery.getLevel())) {
            // 查询是否是叶子节点
            int count = accMenuMapper.selectMenuChildCountByPid(accMenuQuery.getParentId());
            if (count == 0) {
                accMenuMapper.updateByPrimaryKeySelective(updateParentUacMenu);
            }
        }
        return result;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public boolean checkMenuHasChildMenu(Long pid) {
        Preconditions.checkArgument(pid != null, "菜单pid不能为空");

        AccMenu accMenu = new AccMenu();
        accMenu.setIsEnable(true);
        accMenu.setParentId(pid);

        return mapper.selectCount(accMenu) > 0;
    }

    @Override
    public void updateUacMenuStatusById(AccMenuEnableDto accMenuEnableDto, LoginAuthDto loginAuthDto) {
        Long id = accMenuEnableDto.getMenuId();
        Boolean isEnable = accMenuEnableDto.getIsEnable();
        Preconditions.checkArgument(id != null, "菜单ID不能为空");
        Preconditions.checkArgument(isEnable != null, "菜单状态不能为空");

        AccMenu uacMenuQuery = this.selectByKey(id);
        if (MenuConstant.MENU_LEVEL_ROOT.equals(uacMenuQuery.getLevel())) {
            throw new UacBizException(ErrorCodeEnum.UAC10013007);
        }
        // 要处理的菜单集合
        List<AccMenu> menuList = Lists.newArrayList();

        int result;
        if (!isEnable) {
            // 获取菜单以及子菜单
            menuList = this.getAllChildMenuByMenuId(id, true);
            // 禁用菜单以及子菜单
            result = this.disableMenuList(menuList, loginAuthDto);
        } else {
            // 获取菜单、其子菜单以及父菜单
            AccMenu accMenu = new AccMenu();
            accMenu.setParentId(id);
            result = this.selectCount(accMenu);
            // 此菜单含有子菜单
            if (result > 0) {
                menuList = this.getAllChildMenuByMenuId(id, false);
            }
            List<AccMenu> menuListTemp = this.getAllParentMenuByMenuId(id);
            for (AccMenu menu : menuListTemp) {
                if (!menuList.contains(menu)) {
                    menuList.add(menu);
                }
            }
            // 启用菜单、其子菜单以及父菜单
            result = this.enableMenuList(menuList, loginAuthDto);
        }
        if (result < 1) {
            throw new UacBizException(ErrorCodeEnum.UAC10013006, id);
        }
    }

    @Override
    public int enableMenuList(List<AccMenu> menuList, LoginAuthDto loginAuthDto) {
        AccMenu accMenuUpdate = new AccMenu();
        int sum = 0;
        for (AccMenu menu : menuList) {
            accMenuUpdate.setMenuId(menu.getMenuId());
            accMenuUpdate.setIsEnable(true);
            accMenuUpdate.setUpdateId(loginAuthDto.getUserId());
            accMenuUpdate.setUpdateTime(new Date());
            accMenuUpdate.setUpdateTime(new Date());
            int result = mapper.updateByPrimaryKeySelective(accMenuUpdate);
            if (result > 0) {
                sum += 1;
            } else {
                throw new UacBizException(ErrorCodeEnum.UAC10013004, menu.getMenuId());
            }
        }
        return sum;
    }

    @Override
    public int disableMenuList(List<AccMenu> menuList, LoginAuthDto loginAuthDto) {
        AccMenu accMenuUpdate = new AccMenu();
        int sum = 0;
        for (AccMenu menu : menuList) {
            accMenuUpdate.setMenuId(menu.getMenuId());
            accMenuUpdate.setIsEnable(false);
            accMenuUpdate.setUpdateId(loginAuthDto.getUserId());
            accMenuUpdate.setUpdateTime(new Date());
            int result = mapper.updateByPrimaryKeySelective(accMenuUpdate);
            if (result > 0) {
                sum += 1;
            } else {
                throw new UacBizException(ErrorCodeEnum.UAC10013005, menu.getMenuId());
            }
        }
        return sum;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<AccMenu> getAllParentMenuByMenuId(Long menuId) {
        AccMenu accMenuQuery = new AccMenu();
        accMenuQuery.setMenuId(menuId);
        accMenuQuery = mapper.selectOne(accMenuQuery);
        List<AccMenu> accMenuList = Lists.newArrayList();
        accMenuList = buildParentNote(accMenuList, accMenuQuery);
        return accMenuList;
    }

    @Override
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public List<AccMenu> getAllChildMenuByMenuId(Long menuId, Boolean menuIsEnable) {
        AccMenu accMenuQuery = new AccMenu();
        accMenuQuery.setMenuId(menuId);
        accMenuQuery = mapper.selectOne(accMenuQuery);
        List<AccMenu> accMenuList = Lists.newArrayList();
        accMenuList = buildNode(accMenuList, accMenuQuery, menuIsEnable);
        return accMenuList;
    }

    /**
     * 递归获取菜单的子菜单
     */
    private List<AccMenu> buildNode(List<AccMenu> accMenuList, AccMenu accMenu, Boolean menuIsEnable) {
        List<AccMenu> accMenuQueryList = mapper.select(accMenu);
        AccMenu accMenuQuery;
        for (AccMenu menu : accMenuQueryList) {
            // 启用状态
            if (menuIsEnable.equals(menu.getIsEnable()) && !MenuConstant.MENU_LEVEL_ROOT.equals(menu.getLevel())) {
                accMenuList.add(menu);
            }
            accMenuQuery = new AccMenu();
            accMenuQuery.setParentId(menu.getMenuId());
            buildNode(accMenuList, accMenuQuery, menuIsEnable);
        }
        return accMenuList;
    }

    /**
     * 递归获取菜单的父菜单
     */
    private List<AccMenu> buildParentNote(List<AccMenu> accMenuList, AccMenu accMenu) {
        List<AccMenu> accMenuQueryList = mapper.select(accMenu);
        AccMenu accMenuQuery;
        for (AccMenu menu : accMenuQueryList) {
            if (!menu.getIsEnable() && !MenuConstant.MENU_LEVEL_ROOT.equals(menu.getLevel())) {
                // 禁用状态
                accMenuList.add(menu);
            }
            accMenuQuery = new AccMenu();
            accMenuQuery.setMenuId(menu.getParentId());
            buildParentNote(accMenuList, accMenuQuery);
        }
        return accMenuList;
    }

    private List<AccMenu> getMenuListTree(List<AccMenu> accMenuList, Long menuId) {
        AccMenu accMenu = accMenuMapper.selectByPrimaryKey(menuId);
        if (accMenu != null) {
            Long pid = accMenu.getParentId();
            if (pid != null) {
                if (!accMenuList.contains(accMenu)) {  // add by wg 20181221 解决重复部分
                    accMenuList.add(accMenu);
                }
                getMenuListTree(accMenuList, pid);
            }
        }
        return accMenuList;
    }

    @Override
    public List<AccMenu> listMenuListByRoleId(Long roleId) {
        List<AccMenu> menuList = accMenuMapper.listMenuListByRoleId(roleId);
        List<AccMenu> addMenuList = Lists.newArrayList();

        if (PublicUtil.isNotEmpty(menuList)) {
            for (AccMenu accMenu : menuList) {
                // mod by wg 20181221 解决重复部分
//                getMenuListTree(addMenuList, accMenu.getParentId());
                getMenuListTree(addMenuList, accMenu.getMenuId());
            }
        }

        // mod by wg 20181221 解决重复部分
//        menuList.addAll(addMenuList);
//        return new ArrayList<>(new HashSet<>(menuList));
        return new ArrayList<>(new HashSet<>(addMenuList));
    }
}
